import { Scene } from '@babylonjs/core/scene';
import { AbstractControl, IAbstractControlOptions } from './abstract-control';
import { Control } from '@babylonjs/gui/2D/controls/control';
import { CreateHandleTo, IHandle, RemoveHandle } from '../../data/data-handle';
import { Slider } from '@babylonjs/gui/2D/controls/sliders/slider';
import { TextBlock } from '@babylonjs/gui/2D/controls/textBlock';
import { StackPanel } from '@babylonjs/gui/2D/controls/stackPanel';
import { Utils } from '../../tool/utils';


/**
 * 滑动条控件参数对象
 * @param title 页眉（标题）
 * @param unit 值的单位
 * @param isVertical 是否纵向布局
 * @param debug 绘制调试（仅开发中使用）
 * @param value 滑动条的值
 * @param range 值范围
 * @param step 步进值
 * @param showValue 是否显示当前值
 * @param fontFamily 字体
 * @param fontSize 字体大小
 * @param fontColor 字体颜色
 * @param titleWidth 标题宽度
 * @param isSliderVertical 滑动条是否纵向
 * @param sliderWidth 滑动条宽度
 * @param sliderHeight 滑动条高度
 * @param sliderColor 滑动条颜色
 * @param sliderProgressColor 进度条颜色
 * @param sliderThumbColor 滑动块颜色
 * @param spacing 文字块与滑动条的间隔留白
 */
interface IUISliderOptions extends IAbstractControlOptions {
    title?: string,
    unit?: string,
    isVertical?: boolean,
    debug?: boolean,
    value?: number,
    range?: [number, number],
    step?: number,
    showValue?: boolean,
    fontFamily?: string,
    fontSize?: number,
    fontColor?: string,
    titleWidth?: number,
    isSliderVertical?: boolean,
    sliderWidth?: number,
    sliderHeight?: number,
    sliderColor?: string,
    sliderProgressColor?: string,
    sliderThumbColor?: string,
    spacing?: number,
}


/**
 * 滑动条控件
 * @param name 名称
 * @param scene 所在场景
 * @param options 参数对象
 */
class UISlider extends AbstractControl {

    private _changeHandles: Array<IHandle<number>> = [];
    private _startValue!: number;
    private _endValue!: number;
    private _value!: string;
    private _step!: number;
    private _title!: string;
    private _unit!: string;
    private _showValue!: boolean;

    /**
     * 原始滑动条控件
     */
    public slider!: Slider;

    /**
     * 原始标题文字块控件
     */
    public header!: TextBlock;

    constructor(name: string, scene: Scene, options?: IUISliderOptions) {
        super(name, scene, options);
        this.slider.onPointerDownObservable.add(this.breakCamera);
        this.header.onPointerDownObservable.add(this.breakCamera);
    }

    public set isEnabled(enabled: boolean) {
        this.slider.isEnabled = enabled;
        if (this.enabledStyle) this.enabledStyle(enabled, this);
    }
    public get isEnabled(): boolean {
        return this.slider.isEnabled;
    }

    /**
     * 设置滑动条的值
     */
    public set value(value: number) {
        if (!this.isEnabled) return;
        this._value = this.valueString(value);
        this.slider.value = this.valueToRatio();
    }

    /**
     * 获取滑动条的值
     */
    public get value(): number {
        return Number.parseFloat(this._value);
    }

    /**
     * 添加值改变时的回调函数对象
     * @param func 回调函数
     * @returns 回调函数对象
     */
    public onChange(func: { (value: number): void }): IHandle<number> {
        return CreateHandleTo(func, this._changeHandles);
    }

    /**
     * 移除值改变时的回调函数对象
     * @param handle 回调函数对象或其id
     */
    public removeChangeHandle(handle: number | IHandle<number>): void {
        RemoveHandle(handle, this._changeHandles);
    }

    protected initializeControl(name: string, scene: Scene, options: IUISliderOptions): Control {
        const panel = new StackPanel(name + '-panel');
        panel.isVertical = options.isVertical || false;
        if (options.debug) panel.background = '#ff00ff';

        this._startValue = options.range ? options.range[0] : 0;
        this._endValue = options.range ? options.range[1] : 1;
        this._step = options.step || 0;
        this._value = this.valueString(options.value || 0);
        this._title = options.title || 'slider';
        this._unit = options.unit || '';
        this._showValue = options.showValue === undefined ? true : options.showValue;
        const fontSize = options.fontSize || 26;

        const text = this.getHeaderText();
        const header = new TextBlock(name + '-header');
        header.text = text;
        header.fontFamily = options.fontFamily || 'Arial';
        header.fontSize = fontSize;
        header.color = options.fontColor || '#000000';
        header.width = (options.titleWidth || (Utils.byteLength(text) * 0.5 * fontSize)) + 'px';
        header.height = fontSize + 'px';
        header.clipContent = false;
        this.header = header;
        panel.addControl(header);

        const slider = new Slider(name + '-slider');
        slider.minimum = 0;
        slider.maximum = 1;
        slider.value = this.valueToRatio();
        slider.isVertical = options.isSliderVertical || false;
        slider.width = (options.sliderWidth || (fontSize * 5)) + 'px';
        slider.height = (options.sliderHeight || fontSize) + 'px';
        slider.background = options.sliderColor || '#000000';
        slider.color = options.sliderProgressColor || '#000000';
        slider.thumbColor = options.sliderThumbColor || '#ffffff';
        this.slider = slider;
        panel.addControl(slider);

        const space = options.spacing || 5;
        UISlider.autoLayout(panel, header, space, [slider], [slider]);

        return panel;
    }

    protected bindEvent(control: Control, options: IUISliderOptions): void {
        this.slider.onValueChangedObservable.add((value: number) => {
            if (!this.isEnabled) return;
            const val = this.ratioToValue(value);
            this._value = this.valueString(val);
            this.header.text = this.getHeaderText();
            if (!this.avoidEventHandle) this._changeHandles.forEach(handle => handle.callback(this.value));
        });
    }

    protected unbindEvent(control: Control): void {
        super.unbindEvent(control);
        this.slider.onValueChangedObservable.clear();
    }

    /**
     * 转换当前值为比值
     * @returns 比值
     */
    private valueToRatio(): number {
        const value = Number.parseFloat(this._value);
        return (value - this._startValue) / (this._endValue - this._startValue);
    }

    /**
     * 转换比值为值
     * @param ratio 比值
     * @returns 值
     */
    private ratioToValue(ratio: number): number {
        return (this._endValue - this._startValue) * ratio + this._startValue;
    }

    /**
     * 获取标题内容
     * @returns 标题内容
     */
    private getHeaderText(): string {
        if (this._showValue) {
            return this._title + ' ' + this._value + ' ' + this._unit;
        }
        return this._title;
    }

    /**
     * 将值转换为字符串
     * @param value 值
     * @returns 字符串
     */
    private valueString(value: number): string {
        const max = Math.max(this._startValue, this._endValue);
        const min = Math.min(this._startValue, this._endValue);
        const val = Math.max(Math.min(value, max), min);
        const step = this._step;
        if (step === 0) {
            return val + '';
        }
        const v = Utils.normalizeNumber(val, step);
        return Number.parseFloat(v) + '';
    }

}


/**
 * 创建滑动条控件
 * @param name 名称
 * @param scene 所在场景
 * @param options 参数对象
 * @param changeCallback 值改变时的回调函数
 * @returns 滑动条控件
 */
const CreateUISlider = (name: string, scene: Scene, options?: IUISliderOptions, changeCallback?: { (value: number): void }): UISlider => {
    const slider = new UISlider(name, scene, options);
    if (changeCallback) slider.onChange(changeCallback);
    return slider;
};


export { UISlider, CreateUISlider };
export type { IUISliderOptions };